{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Chap2 Object-Oriented Programming"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-06-28T04:23:30.079620Z",
     "start_time": "2019-06-28T04:23:30.071704Z"
    }
   },
   "outputs": [],
   "source": [
    "import numbers\n",
    "import time\n",
    "from math import sqrt\n",
    "from abc import ABCMeta, abstractmethod\n",
    "from typing import TypeVar, Union, Any\n",
    "from random import randint\n",
    "import matplotlib.pyplot as plt\n",
    "Num = TypeVar('Num', int, float)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Reinforcement"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### R-2.4\n",
    "Write a Python class, Flower, that has three instance variables of type str,\n",
    "int, and float, that respectively represent the name of the flower, its num-\n",
    "ber of petals, and its price. Your class must include a constructor method\n",
    "that initializes each variable to an appropriate value, and your class should\n",
    "include methods for setting the value of each type, and retrieving the value\n",
    "of each type.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-06-23T00:44:43.280302Z",
     "start_time": "2019-06-23T00:44:43.269075Z"
    }
   },
   "outputs": [],
   "source": [
    "class Flower:\n",
    "    def __init__(self, name: str, n_petals: int, price: Num) -> None:\n",
    "        self._name = name\n",
    "        self._n_petals = n_petals\n",
    "        self._price = price\n",
    "\n",
    "    def get_name(self) -> str:\n",
    "        '''get flower name'''\n",
    "        return self._name\n",
    "    \n",
    "    def set_name(self, name: str) -> None:\n",
    "        '''set flower name'''\n",
    "        self._name = name\n",
    "\n",
    "    def get_n_petals(self) -> int:\n",
    "        '''get the num of flower'petrals'''\n",
    "        return self._n_petals\n",
    "\n",
    "    def set_n_petals(self, n_petals: int) -> None:\n",
    "        '''set the num of flower'petrals'''        \n",
    "        self._n_petals = n_petals\n",
    "\n",
    "    def get_price(self) -> Num:\n",
    "        '''get flower price'''\n",
    "        return self._price\n",
    "\n",
    "    def set_price(self, price) -> None:\n",
    "        '''set flower price'''\n",
    "        self._price = price"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-06-23T00:55:12.339373Z",
     "start_time": "2019-06-23T00:55:12.334137Z"
    }
   },
   "source": [
    "注意这里，我们一般写自己的class的时候，最后写上docstring和type,这样可以通过`help(Flower)`命令来获取自动生成的文档。不过为方便解题，我们下面有时候会省略掉。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### R-2.5&R-2.6&2.7\n",
    "R-2.5: Use the techniques of Section 1.7 to revise the charge and make payment\n",
    "methods of the CreditCard class to ensure that the caller sends a number\n",
    "as a parameter.\n",
    "\n",
    "\n",
    "R-2.6: If the parameter to the make payment method of the CreditCard class\n",
    "were a negative number, that would have the effect of raising the balance\n",
    "on the account. Revise the implementation so that it raises a ValueError if\n",
    "a negative value is sent.\n",
    "\n",
    "R-2.7: The CreditCard class of Section 2.3 initializes the balance of a new ac-\n",
    "count to zero. Modify that class so that a new account can be given a\n",
    "nonzero balance using an optional fifth parameter to the constructor. The\n",
    "four-parameter constructor syntax should continue to produce an account\n",
    "with zero balance."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-06-23T01:36:46.905697Z",
     "start_time": "2019-06-23T01:36:46.890724Z"
    }
   },
   "outputs": [],
   "source": [
    "class CrediCart:\n",
    "    def __init__(self, customer, bank, acnt, limit, balance=0):\n",
    "        self._cunstomer = customer\n",
    "        self._bank = bank\n",
    "        self._acnt = acnt\n",
    "        self._limit = limit\n",
    "        self._balance = balance\n",
    "\n",
    "    def charge(self, price: Num):\n",
    "        \"\"\"Charge given price to the card, assuming sufficient credit limit.\n",
    "        Return True if charge was processed; False if charge was denied.\n",
    "        \"\"\"\n",
    "        try: \n",
    "            assert isinstance(price, (int, float, complex))\n",
    "        except AssertionError:\n",
    "            print(\"The price must be a number!\")\n",
    "            # exit the function\n",
    "            return\n",
    "        # if charge would exceed limit,\n",
    "        if price + self._balance > self._limit:\n",
    "            return False\n",
    "        # cannot accept charge\n",
    "        else:\n",
    "            self._balance += price\n",
    "            return True\n",
    "\n",
    "    def make_payment(self, amount):\n",
    "        # is a number or not\n",
    "        try: \n",
    "            assert isinstance(amount, numbers.Number)\n",
    "        except AssertionError:\n",
    "            print(\"The amount must be a number!\")\n",
    "            # exit the function\n",
    "            return\n",
    "        # is positive or not\n",
    "        if amount < 0:\n",
    "            raise ValueError(\"amount must be a positive number\")\n",
    "        \"\"\"Process customer payment that reduces balance.\"\"\"\n",
    "        self._balance -= amount"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "注意这里我们无非就是加进去一个异常检测，检测给的参数是不数字，这里对于两个检测，我们给出两个不同的方法，`isinstance(price, (int, float, complex))`与`isinstance(amount, numbers.Number)`，二者均可，参考[这里](https://stackoverflow.com/questions/11204789/how-to-properly-use-pythons-isinstance-to-check-if-a-variable-is-a-number)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### R-2.9 -> R-2.15\n",
    "\n",
    "主要就是定义一个`Vector`类，并实现向量的一些运算。\n",
    "\n",
    "注意下面在类内部使用type hint的方法，参考[Type of the same class inside the class](https://github.com/python/mypy/issues/3661)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-06-23T04:03:17.929090Z",
     "start_time": "2019-06-23T04:03:17.894714Z"
    }
   },
   "outputs": [],
   "source": [
    "class Vector:\n",
    "    def __init__(self, data_or_n: Any) -> None:\n",
    "        if isinstance(data_or_n, int):\n",
    "            self._data = [0] * data_or_n\n",
    "            self._len = data_or_n\n",
    "        elif isinstance(data_or_n, list):\n",
    "            self._data = data_or_n\n",
    "            self._len = len(data_or_n)\n",
    "        else:\n",
    "            raise ValueError(\"Vector must be initialized by a int or list!\")\n",
    "\n",
    "\n",
    "    def __repr__(self) -> str:\n",
    "        return repr(self._data).replace('[', '<').replace(']', '>')\n",
    "    \n",
    "    def __len__(self):\n",
    "        return self._len\n",
    "\n",
    "    def __getitem__(self, index: int) -> Num:\n",
    "        return self._data[index]\n",
    "    \n",
    "    def __setitem__(self, index: int, value: Num) -> None:\n",
    "        self._data[index] = value\n",
    "    \n",
    "    def __add__(self, v: 'Vector') -> 'Vector':\n",
    "        \"\"\"\n",
    "        >>> v1 = Vector([1, 2, 3])\n",
    "        >>> v2 = Vector([1, 1, 1])\n",
    "        >>> v1 + v2\n",
    "        <2, 3, 4>\n",
    "        \"\"\"        \n",
    "        assert len(self) == len(v)\n",
    "        result = Vector(self._len)\n",
    "        for i in range(len(self)):\n",
    "            result[i] = self._data[i] + v[i]\n",
    "        return result\n",
    "    \n",
    "    def __radd__(self, v: 'Vector') -> 'Vector':\n",
    "        return self.__add__(v)\n",
    "        \n",
    "\n",
    "    def __sub__(self, v: 'Vector') -> 'Vector':\n",
    "        \"\"\"\n",
    "        >>> v1 = Vector([1, 2, 3])\n",
    "        >>> v2 = Vector([1, 1, 1])\n",
    "        >>> v1 - v2\n",
    "        <0, 1, 2>\n",
    "        \"\"\"\n",
    "        assert len(self) == len(v)\n",
    "        result = Vector(self._len)\n",
    "        for i in range(len(self)):\n",
    "            result[i] = self._data[i] - v[i]\n",
    "        return result\n",
    "\n",
    "    def __neg__(self) -> 'Vector':\n",
    "        \"\"\"\n",
    "        >>> -Vector([1, 2, 3])\n",
    "        <-1, -2, -3>\n",
    "        \"\"\"\n",
    "        result = Vector(self._len)\n",
    "        for index, value in enumerate(self._data):\n",
    "            result[index] = -value\n",
    "        return result\n",
    "    \n",
    "    def __mul__(self, factor: Union[int, 'Vector']) -> Union[int, 'Vector']:\n",
    "        \"\"\"multiply with an int or vector.\n",
    "        >>> v1 = Vector([1, 2, 3])\n",
    "        >>> v1 * 2\n",
    "        <2, 4, 6>\n",
    "        >>> 2 * v1\n",
    "        <2, 4, 6>\n",
    "        >>> v2 = Vector([1, 1, 1])\n",
    "        >>> v1 * v2\n",
    "        6\n",
    "        \"\"\"\n",
    "        if isinstance(factor, Vector):\n",
    "            assert len(self) == len(factor)\n",
    "            result = 0\n",
    "            for i in range(self._len):\n",
    "                result += self._data[i] * factor[i]\n",
    "            return result\n",
    "        else:\n",
    "            result = Vector(self._len)\n",
    "            for index, value in enumerate(self._data):\n",
    "                result[index] = factor * value\n",
    "            return result                   \n",
    "\n",
    "    def __rmul__(self, factor: Union[int, 'Vector']) -> Union[int, 'Vector']:\n",
    "        return self.__mul__(factor)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上述代码用`pytest`， `pyright`的检验均全部通过。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### R-2.16\n",
    "Our Range class, from Section 2.3.5, relies on the formula\n",
    "max(0, (stop − start + step − 1) // step)\n",
    "to compute the number of elements in the range. It is not immediately ev-\n",
    "ident why this formula provides the correct calculation, even if assuming\n",
    "a positive step size. Justify this formula, in your own words."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "$$max(0, (stop - start + step - 1) // step)$$\n",
    "将其化简，前面的1，可以看作第一个值，\n",
    "$$max(0, 1 + (stop - start - 1) // step)$$\n",
    "之后，本应该加上$(stop - start)  // step$，但是考虑到其是不包括`stop`处的数值的，考虑到最小的步长为1（必须为整数），所以实际的终止点为`stop-1`, 故加上$(stop - start - 1) //  step$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### R-2.18\n",
    "Give a short fragment of Python code that uses the progression classes\n",
    "from Section 2.4.2 to find the 8 th value of a Fibonacci progression that\n",
    "starts with 2 and 2 as its first two values."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-06-28T04:17:17.303449Z",
     "start_time": "2019-06-28T04:17:17.283614Z"
    }
   },
   "outputs": [],
   "source": [
    "# code in 2.4.2\n",
    "class Progression:\n",
    "    def __init__(self, start=0):\n",
    "        self._current = start\n",
    "    \n",
    "    def _advance(self):\n",
    "        self._current += 1\n",
    "\n",
    "    def __next__(self):\n",
    "        if self._current is None:\n",
    "            raise StopIteration\n",
    "        else:\n",
    "            answer = self._current\n",
    "            self._advance()\n",
    "            return answer\n",
    "\n",
    "    def __iter__(self):\n",
    "        return self\n",
    "\n",
    "    def print_progression(self, n):\n",
    "        print(' '.join(str(next(self)) for i in range(n)))\n",
    "\n",
    "\n",
    "class FibonacciProgression(Progression):\n",
    "    def __init__(self, first=0, second=1):\n",
    "        # initialize base class\n",
    "        # start progression at first\n",
    "        super().__init__(first)\n",
    "        self._prev = second - first\n",
    "    \n",
    "    def _advance(self):\n",
    "        self._prev, self._current = self._current, self._current + self._prev"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-06-28T02:19:05.230116Z",
     "start_time": "2019-06-28T02:19:05.220147Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2 2 4 6 10 16 26 42\n"
     ]
    }
   ],
   "source": [
    "# solution of R-2.18\n",
    "fib = FibonacciProgression(2, 2)\n",
    "fib.print_progression(8)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "由此得到，问题的答案为42（出现了，宇宙的终极答案:-)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### R-2.19\n",
    "When using the ArithmeticProgression class of Section 2.4.2 with an in-\n",
    "crement of 128 and a start of 0, how many calls to next can we make\n",
    "before we reach an integer of $2^{63}$ or larger?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-06-28T02:38:53.729432Z",
     "start_time": "2019-06-28T02:38:53.717500Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "72057594037927936"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "steps = 2 ** (63 - 7)\n",
    "steps"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Creativity"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### C-2.25\n",
    "Exercise R-2.12 uses the mul method to support multiplying a Vector\n",
    "by a number, while Exercise R-2.14 uses the mul method to support\n",
    "computing a dot product of two vectors. Give a single implementation of\n",
    "Vector. mul that uses run-time type checking to support both syntaxes\n",
    "u v and u k, where u and v designate vector instances and k represents\n",
    "a number."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "其实在上面我们已经给出了这种写法，参考R-2.9 -> R-2.15"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### C-2.26 \n",
    "The SequenceIterator class of Section 2.3.4 provides what is known as a\n",
    "forward iterator. Implement a class named ReversedSequenceIterator that\n",
    "serves as a reverse iterator for any Python sequence type. The first call to\n",
    "next should return the last element of the sequence, the second call to next\n",
    "should return the second-to-last element, and so forth."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-06-28T03:05:19.559979Z",
     "start_time": "2019-06-28T03:05:19.554123Z"
    }
   },
   "outputs": [],
   "source": [
    "class ReversedSequenceIterator:\n",
    "    \"\"\"\n",
    "    A reversed iterator for Python's sequence types.\n",
    "    \"\"\"\n",
    "    def __init__(self, sequence):\n",
    "        \"\"\"Create an iterator for the given sequence\"\"\"\n",
    "        self._seq = sequence\n",
    "        self._k = len(sequence)\n",
    "\n",
    "    def __next__(self):\n",
    "        \"\"\"Return the next element(with reversed order), or else\n",
    "        raise StopIteration error\"\"\"\n",
    "        self._k  -= 1\n",
    "        if self._k >= 0:\n",
    "            return self._seq[self._k]\n",
    "        else:\n",
    "            raise StopIteration\n",
    "\n",
    "    def __iter__(self):\n",
    "        return self"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-06-28T03:05:55.700009Z",
     "start_time": "2019-06-28T03:05:55.692266Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5\n",
      "3\n",
      "1\n"
     ]
    }
   ],
   "source": [
    "rev_seq = ReversedSequenceIterator([1, 3, 5])\n",
    "for i in rev_seq:\n",
    "    print(i)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### C-2.27 \n",
    "In Section 2.3.5, we note that our version of the Range class has im-\n",
    "plicit support for iteration, due to its explicit support of both len\n",
    "and getitem . The class also receives implicit support of the Boolean\n",
    "test, “k in r” for Range r. This test is evaluated based on a forward itera-\n",
    "tion through the range, as evidenced by the relative quickness of the test\n",
    "2 in Range(10000000) versus 9999999 in Range(10000000). Provide a\n",
    "more efficient implementation of the contains method to determine\n",
    "whether a particular value lies within a given range. The running time of\n",
    "your method should be independent of the length of the range."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-06-28T03:50:37.930619Z",
     "start_time": "2019-06-28T03:50:37.910632Z"
    }
   },
   "outputs": [],
   "source": [
    "class Range:\n",
    "    \"\"\"A class that mimic's the build-in range class\"\"\"\n",
    "    def __init__(self, start, stop=None, step=1):\n",
    "        if step == 0:\n",
    "            raise ValueError('step cannot ne 0')\n",
    "        # special case for range(n)\n",
    "        if stop is None:\n",
    "            start, stop = 0, start\n",
    "        # calculate the effective length once\n",
    "        self._length = max(0, (step - start + step - 1) // step)\n",
    "        # need knowledge of the start and step(but not stop) to support __getitem__\n",
    "        self._start = start\n",
    "        self._step = step\n",
    "        # for __contains\n",
    "        self._stop = stop\n",
    "\n",
    "    def __len__(self):\n",
    "        return self._length\n",
    "\n",
    "    def __getitem__(self, k):\n",
    "        # negative index\n",
    "        if k < 0:\n",
    "            k += self._length\n",
    "\n",
    "        if not 0 <= k < self._length:\n",
    "            raise IndexError(\"index out of range\")\n",
    "\n",
    "        return self._start + k * self._step\n",
    "    \n",
    "    def __contains__(self, x):\n",
    "        if not self._start <= x < self._stop:\n",
    "            return False\n",
    "        if (x - self._start) % self._step == 0:\n",
    "            return True\n",
    "        else:\n",
    "            return False\n",
    "    \n",
    "    @staticmethod\n",
    "    def test_contains(start=0, stop=10000000, step=1, test_n=50):\n",
    "        xs = sorted([randint(start, stop) for i in range(test_n)])\n",
    "        times = []\n",
    "        my_range = Range(start, stop, step)\n",
    "        for x in xs:\n",
    "            time_s = time.time()\n",
    "            x in my_range\n",
    "            times.append(time.time() - time_s)\n",
    "        plt.plot(xs, times)\n",
    "        plt.ylim(0, 0.00002)\n",
    "        plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-06-28T03:50:40.309603Z",
     "start_time": "2019-06-28T03:50:40.144819Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7f8669475128>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "r = Range(0, 10000000, 1)\n",
    "r.test_contains()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-06-28T03:55:02.922063Z",
     "start_time": "2019-06-28T03:55:01.694016Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Intel(R) Core(TM) i5-5200U CPU @ 2.20GHz'"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 测试环境（需要先安装py-cupinfo库）\n",
    "import cpuinfo\n",
    "cpuinfo.get_cpu_info()['brand']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### C-2.31\n",
    "Write a Python class that extends the Progression class so that each value\n",
    "in the progression is the absolute value of the difference between the pre-\n",
    "vious two values. You should include a constructor that accepts a pair of\n",
    "numbers as the first two values, using 2 and 200 as the defaults."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-06-28T04:17:20.659218Z",
     "start_time": "2019-06-28T04:17:20.648277Z"
    }
   },
   "outputs": [],
   "source": [
    "class AbsdiffPrograssion(Progression):\n",
    "    def __init__(self, first=2, second=200):\n",
    "        super().__init__(first)\n",
    "        self._prev1 = None\n",
    "        self._prev2 = None\n",
    "        # for starting\n",
    "        self._second = second\n",
    "        self._count = 1\n",
    "\n",
    "    def _advance(self):\n",
    "        # for starting\n",
    "        if self._count == 1:\n",
    "            self._prev1 = self._current\n",
    "            self._current = self._second\n",
    "        else:\n",
    "            self._prev1, self._prev2 = self._current, self._prev1\n",
    "            self._current = abs(self._prev1 - self._prev2)\n",
    "        self._count += 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-06-28T04:17:27.977619Z",
     "start_time": "2019-06-28T04:17:27.972631Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2 200 198 2 196 194 2 192 190 2\n"
     ]
    }
   ],
   "source": [
    "t = AbsdiffPrograssion()\n",
    "t.print_progression(10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### C-2.32\n",
    "Write a Python class that extends the Progression class so that each value\n",
    "in the progression is the square root of the previous value. (Note that\n",
    "you can no longer represent each value with an integer.) Your construc-\n",
    "tor should accept an optional parameter specifying the start value, using\n",
    "65, 536 as a default."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-06-28T04:23:14.315061Z",
     "start_time": "2019-06-28T04:23:14.302081Z"
    }
   },
   "outputs": [],
   "source": [
    "class SquareRootProgression(Progression):\n",
    "    def __init__(self, start=65536):\n",
    "        super().__init__(start)\n",
    "\n",
    "    def _advance(self):\n",
    "        self._current = sqrt(self._current)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-06-28T04:23:35.170619Z",
     "start_time": "2019-06-28T04:23:35.159627Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "65536 256.0 16.0 4.0 2.0 1.4142135623730951 1.189207115002721 1.0905077326652577 1.0442737824274138 1.0218971486541166\n"
     ]
    }
   ],
   "source": [
    "s = SquareRootProgression()\n",
    "s.print_progression(10)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.8"
  },
  "toc": {
   "nav_menu": {},
   "number_sections": false,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {
    "height": "calc(100% - 180px)",
    "left": "10px",
    "top": "150px",
    "width": "273.2px"
   },
   "toc_section_display": true,
   "toc_window_display": true
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
